package com.inyourcode.excel.iml;

import com.alibaba.fastjson.JSONArray;
import com.google.common.base.Strings;
import com.google.common.io.Resources;
import com.inyourcode.excel.ExportContext;
import com.inyourcode.excel.ExportException;
import com.inyourcode.excel.api.IExporter;
import com.inyourcode.excel.model.JavaExportClass;
import com.inyourcode.excel.model.SheetDataModel;
import com.inyourcode.excel.model.column.CommentColData;
import com.inyourcode.excel.model.column.NameColData;
import com.inyourcode.excel.model.column.TypeColData;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * java类导出器
 * @author JackLei
 */
public class JavaExporter implements IExporter {
    private static final Logger LOGGER = LoggerFactory.getLogger(JavaExporter.class);
    /** 配置属性的key: java类包名 */
    private static final String KEY_PACKAGE_PATH = "java.out.package";
    /** 配置属性的key: java类导出路径   */
    private static final String KEY_JAVA_OUT_PATH = "java.out.path";
    /** 配置属性的key: java类导出模版 */
    private static final String FILE_TEMPLATE_NAME = "java-export.ftl";
    /** java类需要引入的包名 */
    private static final Set<String> IMPORTS = new HashSet<>(Arrays.asList( "com.alibaba.fastjson.JSONArray",
                                                                            "com.inyourcode.excel.serializer.JavaExcelList",
                                                                            "com.inyourcode.excel.serializer.JavaExcelEnum",
                                                                            "com.inyourcode.excel.api.ExcelTable",
                                                                            "com.inyourcode.excel.serializer.JavaEnumSerializer",
                                                                            "com.inyourcode.excel.serializer.JavaListSerializer",
                                                                            "com.alibaba.fastjson.annotation.JSONField"));

    @Override
    public boolean check(ExportContext context) throws ExportException {
        String outPath = context.getProperties().getProperty(KEY_JAVA_OUT_PATH);
        return true;
    }

    @Override
    public void export(ExportContext context) throws ExportException {

        Properties properties = context.getProperties();
        String packageName = properties.getProperty(KEY_PACKAGE_PATH);
        String javaOutPath = properties.getProperty(KEY_JAVA_OUT_PATH);
        if (javaOutPath == null || javaOutPath.isEmpty())
        {
            return;
        }

        //初始化导出模版
        Map<String, SheetDataModel> sheetDataModelMap = context.getSheetDataModelMap();
        Template template;
        try {
            String templateString = Resources.toString(this.getClass().getClassLoader().getResource(FILE_TEMPLATE_NAME), Charset.forName("UTF-8"));
            Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
            StringTemplateLoader loader = new StringTemplateLoader();
            loader.putTemplate(FILE_TEMPLATE_NAME, templateString);
            cfg.setTemplateLoader(loader);
            cfg.setDefaultEncoding("UTF-8");

            template = cfg.getTemplate(FILE_TEMPLATE_NAME);
        } catch (IOException e) {
            LOGGER.error("init template configuration error, template file name:{}", FILE_TEMPLATE_NAME);
            throw new ExportException("init template configuration error", e);
        }

        for (Map.Entry<String, SheetDataModel> entry : sheetDataModelMap.entrySet()) {
            String name = entry.getKey();
            SheetDataModel model = entry.getValue();

            //初始化导出的java类的数据模型
            String javaClassName = name.substring(0,1).toUpperCase() + name.substring(1);
            JavaExportClass javaExportClass = new JavaExportClass(packageName, javaClassName);
            javaExportClass.setImports(IMPORTS);
            javaExportClass.setDataFileName(name + ".json");

            //初始化导出的java类的属性
            CommentColData[] commentHeader = model.getCommentHeader();
            NameColData[] nameHeader = model.getNameHeader();
            TypeColData[] typeHeader = model.getTypeHeader();
            try {
                for (int index = 0; index < nameHeader.length; index++) {
                    if (commentHeader[index].isIgnore())
                    {
                        continue;
                    }
                    String fieldName = commentHeader[index].getVal().toString();
                    String type = "";
                    /**指定json序列化器*/
                    int serializeType = 0;
                    if (typeHeader[index].isFloat()) {
                        type = "float";
                    } else if (typeHeader[index].isInt()) {
                        type = "int";
                    } else if (typeHeader[index].isString()) {
                        type = "String";
                    } else if (typeHeader[index].isIntArry()) {
                        type = "JavaExcelList<Integer>";
                        serializeType = 1;
                    } else if (typeHeader[index].isFloatArray()) {
                        type = "JavaExcelList<Float>";
                        serializeType = 1;
                    } else if (typeHeader[index].isStringArray()) {
                        type = "JavaExcelList<String>";
                        serializeType = 1;
                    } else if(typeHeader[index].isListIntArray()) {
                        type = "JavaExcelList<int[]>";
                        serializeType = 1;
                    }else if (typeHeader[index].isEnum()) {
                        serializeType = 2;
                        String enumHeaderStr = nameHeader[index].getVal().toString();
                        type = javaClassName + "Enum" + enumHeaderStr.substring(0, 1).toUpperCase() + enumHeaderStr.substring(1);

                        JavaExportClass.JavaExportEnum javaExportEnumClazz = new JavaExportClass.JavaExportEnum();
                        javaExportEnumClazz.setEnumClassName(type);

                        String enumCommentStr = commentHeader[index].getVal().toString();
                        try {
                            int enumStartIndex = enumCommentStr.indexOf("[");
                            int enumEndIndex = enumCommentStr.lastIndexOf("]");
                            if (enumStartIndex == -1 || enumEndIndex == -1) {
                                LOGGER.error("export java enum error, enumCommentStr len not match，name:{}, enum:{}", name, enumCommentStr);
                                continue;
                            }

                            fieldName = enumCommentStr.substring(0, enumStartIndex).replaceAll("\n","");

                            String enumStr = enumCommentStr.substring(enumStartIndex, enumEndIndex + 1);
                            JSONArray enumJsonArray = JSONArray.parseArray(enumStr);
                            for (int i = 0; i < enumJsonArray.size(); i++ ) {
                                String enumJsonString = enumJsonArray.getString(i);
                                String[] enumFieldArray = enumJsonString.split("\\|");
                                JavaExportClass.JavaExportEnumElement enumElementClazz = new JavaExportClass.JavaExportEnumElement();
                                enumElementClazz.setType(Integer.valueOf(enumFieldArray[0]));
                                enumElementClazz.setDesc(enumFieldArray[1]);
                                enumElementClazz.setComment(enumFieldArray[2]);
                                javaExportEnumClazz.getFields().add(enumElementClazz);
                            }

                            javaExportClass.getEnumClassList().add(javaExportEnumClazz);
                        } catch (Exception ex) {
                            LOGGER.error("export java enum error, This format configuration is incorrect，name:{}, enum:{}", name, enumCommentStr);
                            continue;
                        }

                    } else {
                        LOGGER.error("export java error, type not found, type:{}, data:{},name:{}", typeHeader[index].getVal(), typeHeader[index], name);
                        continue;
                    }

                    JavaExportClass.JavaExportField javaExportField = new JavaExportClass.JavaExportField(type, nameHeader[index].getVal().toString(), fieldName, serializeType);
                    javaExportClass.getFields().add(javaExportField);
                }

                String genJavaPackageName = javaExportClass.getPackageName();
                String genJavaClassName = javaExportClass.getJavaClassName() + ".java";
                String parentPath = javaOutPath.concat(Stream.of(genJavaPackageName.split("\\."))
                        .collect(Collectors.joining("/", "/", "/")));

                //目录不存在，先创建
                File parentFile = new File(parentPath);
                if (!parentFile.exists()) {
                    parentFile.mkdirs();
                }

                //创建java文件
                String fileOutPath = parentPath + File.separator + genJavaClassName;
                File fileOut = new File(fileOutPath);
                if (!fileOut.exists()) {
                    fileOut.createNewFile();
                }

                LOGGER.info("export java class,path = {}", fileOutPath);

                OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(fileOut));
                template.process(javaExportClass, outputStreamWriter);
            }catch (Exception ex) {
                LOGGER.error("sheentName:{}, export java failed", name);
                throw new ExportException("export java error", ex);
            }

        }
    }

}
